package design_pattern.WaitSet;

import org.omg.CORBA.TRANSACTION_MODE;

import java.util.Optional;
import java.util.stream.IntStream;

/**
 * @Author: 蔡翔
 * @Date: 2019/10/24 9:06
 * @Version 1.0
 *
 * 1。所有对象都有一个wait set，用来存放调用了该对象wait方法之后 进入block状态的线程。
 * 2. 线程被notify之后，不一定立即得到执行。
 * 3. 线程从wait set 中被唤醒顺序不一定是 FIFO(先进先出的)
 * 4.线程被唤醒后，必须重新获取锁。
 */
public class WaitSet {
    private static final Object LOCK = new Object();
    private static void work(){
        synchronized (LOCK){
            System.out.println("begin");
            try {
                System.out.println("Thread will coming");
                //调用waiy()后，线程会进入LOCK的wait set 这时候线程会释放所有的 synchronize，
                // 别人唤醒后这个线程会去重新拿这个锁,拿到这个锁后不会再重头去执行System.out.println("begin"); 直接到原来的那个位置后，后续执行。
                LOCK.wait();
            }catch (Exception e){
                e.printStackTrace();
            }
            System.out.println("Thread will out.");
        }
    }

    //wait 和sleep的区别  wait会把这个锁给释放（放弃cpu的执行权，这时候这个线程处于block状态），但是sleep会抓着锁不放
    // 怎么去唤醒这个wait的线程,唤醒之后怎么出来？
    public static void main(String[] args) {
        new Thread(){
            @Override
            public void run(){
                work();
            }
        }.start();
        try {
            Thread.sleep(1000);
        }catch (Exception e){
            e.printStackTrace();
        }
        synchronized (LOCK){
            //注意 必须先拿到LOCK 才能LOCK.notify();
            LOCK.notify();
        }


        IntStream.rangeClosed(1,10)
                .forEach(i->{
                    new Thread(String.valueOf(i)){
                        @Override
                        public void run(){
                            synchronized (LOCK){
                                try {
                                    Optional.of(Thread.currentThread().getName()+"will come to waitset")
                                            .ifPresent(System.out::println);
                                    // 这个线程 执行LOCK wait()方法后,会把自己放到 LOCK对象 的waitset里面去，（推导 任何对象都有一个waitset）
                                    // 释放这个锁，1.其他地方notify 或者notifyall；2.wait()执行完；3，interrupt这个thread
                                    LOCK.wait();
                                    //wait 之后 就是就是可执行状态，并且没有释放锁
                                    Optional.of(Thread.currentThread().getName()+"will leave to waitset")
                                            .ifPresent(System.out::println);
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    }.start();
                });

        // 其实真正执行顺序是这样的，不一定要等线程全部启动 阻塞在哪里，下面这里notirfy才会执行。
        // 实际上是 还没等线程创建完成，LOCK锁的 notify 就开始把waitset 里面的线程给轰出去了

        // 解决上述问题，一个办法是.让这个主线程停止10000ms，Thread.sleep()会抓住当前线程不放，还是占用cpu。为下面代码执行让出时间（保证按顺序执行）
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        IntStream.rangeClosed(1,10)
                .forEach(i->{
                    synchronized (LOCK){
                        //对lock锁notify() 那么所以阻塞在这里的线程都会被唤醒。
                        LOCK.notify();
                        try {
                            Thread.sleep(1000);
                        }catch (Exception e){
                            e.printStackTrace();
                        }
                    }
                });
    }
}
